<?php

namespace frontend\models;

use Yii;
use yii\db\Exception;
use common\models\app\AppToken;
use common\models\app\AppDevice;
use common\models\app\AppClient;
use common\models\app\AppVersion;

/**
 * This is the model class for table "{{%app_tokens}}".
 *
 */
class AccessToken extends \common\base\Model {

    /**
     * $appid 应用ID
     * @var string
     */
    public $appid;

    /**
     * $timestamp 时间戳
     * @var string
     */
    public $timestamp;

    /**
     * $device 设备ID
     * @var string
     */
    public $sn;

    /**
     * $os 系统
     * @var string
     */
    public $os;

    /**
     * $model 设备机型
     * @var string
     */
    public $model;

    /**
     * $version build版本号
     * @var integer
     */
    public $build;

    /**
     * $nonce 随即串
     * @var string
     */
    public $nonce;

    /**
     * $sign 签名
     * @var string
     */
    public $sign;
    private $_client = null;

    /**
     * @inheritdoc
     */
    public function rules() {
        return [
            [['appid', 'sn', 'os', 'build', 'nonce', 'sign', 'timestamp'], 'required'],
            [['build', 'timestamp'], 'integer'],
            [['sn', 'model'], 'string', 'max' => 64],
            [['os', 'nonce'], 'string', 'max' => 32],
            //验证请求签名
            ['appid', 'validateAppId'],
            //验证请求签名
            ['build', 'validateBuild'],
            //验证请求签名
            ['sign', 'validateSignature'],
        ];
    }

    /**
     * Validates the APPID.
     * 验证app_id是否为该商户本身
     *
     * @param string $attribute the attribute currently being validated
     */
    public function validateAppId($attribute) {
        if (!$this->hasErrors()) {
            $client = $this->getClient();
            if (!$client) {
                $this->addError($attribute, '未签约商户！');
            } else {
                $this->_client = $client;
            }
        }
    }

    /**
     * Validates the Build no.
     * 验证build是否正确
     *
     * @param string $attribute the attribute currently being validated
     */
    public function validateBuild($attribute) {
        if (!$this->hasErrors()) {
            $version = AppVersion::find()->where([
                'build' => $this->build,
                'os' => $this->os,
            ])->andWhere(['>', 'status', AppVersion::STATUS_DEFAULT])
            ->exists();
            if (!$version) {
                $this->addError($attribute, '请求的版本已不提供支持！');
            }
        }
    }

    /**
     * Validates the signature.
     * 校验请求的签名
     *
     * @param string $attribute the attribute currently being validated
     */
    public function validateSignature($attribute) {
        if (!$this->hasErrors()) {
            $client = $this->getClient();
            $params = $this->attributes;
            unset($params['sign']);
            unset($params['nonce']);
            ksort($params);
            $string = implode('', $params);
            $signature = hash('sha256', $client->secret . $string . $this->nonce);
            if ($signature !== $this->sign) {
                $this->addError($attribute, '签名校验失败！' . $signature);
            }
        }
    }

    /**
     * Find Partner by [[appid]]
     *
     * @return \common\models\app\AppClient|null
     */
    protected function getClient() {
        if ($this->_client === null) {
            $this->_client = AppClient::findByAppId($this->appid);
        }
        return $this->_client;
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels() {
        return [
            'id' => 'ID',
            'appid' => 'App ID',
            'sn' => 'sn',
            'model' => '设备型号',
            'os' => 'os',
            'build' => 'build',
            'nonce' => 'nonce',
            'timestamp' => 'timestamp',
            'sign' => 'sign',
        ];
    }

    public function authent(&$msg) {
        if (!$this->validate()) {
            $msg = $this->errorMsg ?: '请求信息校验失败！';
            return false;
        }
        //开启事务
        $transaction = Yii::$app->db->beginTransaction();
        try {
            $device = AppDevice::findOrNew([
                'sn' => $this->sn,
                'os' => $this->os,
            ]);
            $device->attributes = [
                'build' => $this->build,
                'model' => $this->model,
            ];
            if (!$device->save()) {
                throw new Exception('设备添加失败！' . $device->errorMsg);
            }
            $token = AppToken::findOrNew([
                'client_id' => $this->client->id,
                'device_id' => $device->id,
            ]);
            $token->generateAccessToken();
            if (!$token->save()) {
                throw new Exception('Access Token 生成失败！' . $token->errorMsg);
            }
            $transaction->commit();
        } catch (Exception $e) {
            $transaction->rollBack();
            $msg = $e->getMessage();
            return false;
        }

        return [
            'access_token' => $token->access_token,
            'expired_at' => $token->expired_at,
        ];
    }

}
